package wang.chunfan.springcloud.learning.hystrix.server.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;
import wang.chunfan.springcloud.learning.hystrix.server.model.Result;
import wang.chunfan.springcloud.learning.hystrix.server.model.User;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @version 1.0
 * @Author wangchunfan
 * @Date 2020/5/23 9:17
 **/
@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    private Logger LOGGER = LoggerFactory.getLogger(UserService.class);

    @Value("${server-url.user-server}")
    private String userServerUrl;
    // start ======== 服务降级演示

    // 没有使用服务降级
    public Result testNoFallback(Long id) {
        return restTemplate.getForObject(userServerUrl + "/user/{1}", Result.class, id);
    }

    // 使用服务降级
    @HystrixCommand(fallbackMethod = "fallbackMethod1")
    public Result testFallback(Long id) {
        return restTemplate.getForObject(userServerUrl + "/user/{1}", Result.class, id);
    }


    // 声明的参数需要包含controller的声明参数
    public Result fallbackMethod1(@PathVariable Long id) {
        return new Result("服务调用失败", 500);
    }

    // end ========= 服务降级演示

    // start ======= 忽略某些异常降级
    @HystrixCommand(fallbackMethod = "fallbackMethod2", ignoreExceptions = {NullPointerException.class})
    public Result testIgnoreException(Long id) {
        if (id == 1) {
            throw new IndexOutOfBoundsException();
        } else if (id == 2) {
            throw new NullPointerException();
        }
        return restTemplate.getForObject(userServerUrl + "/user/{1}", Result.class, id);
    }

    public Result fallbackMethod2(@PathVariable Long id, Throwable e) {
        LOGGER.error("id {} ,throwable class:{}", id, e.getClass());
        return new Result("服务调用失败", 500);
    }

    // end ========== 忽略某些异常降级

    // start ======== 请求缓存
    @CacheResult(cacheKeyMethod = "getCacheKey")
    @HystrixCommand(fallbackMethod = "fallbackMethod2", commandKey = "testCache")
    public Result testCache(Long id) {
        LOGGER.info("getCache id:{}", id);
        return restTemplate.getForObject(userServerUrl + "/user/{1}", Result.class, id);
    }

    @HystrixCommand
    @CacheRemove(commandKey = "testCache", cacheKeyMethod = "getCacheKey")
    public Result cacheRemove(Long id) {
        LOGGER.info("removeCache id: {}", id);
        restTemplate.delete(userServerUrl + "/user/{1}", id);
        return new Result();
    }

    public String getCacheKey(Long id) {
        return String.valueOf(id);
    }
    // end ======== 请求缓存

    // start ======= 请求合并
    @HystrixCollapser(batchMethod = "listUsersByIds", collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "100")
    })
    public Future<User> getUserFuture(Long id) {
        return new AsyncResult<User>() {
            @Override
            public User invoke() {
                Result result = restTemplate.getForObject(userServerUrl + "/user/{1}", Result.class, id);
                Map data = (Map) result.getData();
                User user = BeanUtil.mapToBean(data, User.class, true);
                LOGGER.info("getUserById username:{}", user.getUsername());
                return user;
            }
        };
    }

    @HystrixCommand
    public List<User> listUsersByIds(List<Long> ids) {
        LOGGER.info("listUsersByIDs: {}", ids);
        Result result = restTemplate.getForObject(userServerUrl + "/user/list?ids={1}", Result.class, CollUtil.join(ids, ","));
        return (List<User>) result.getData();
    }
    // end ========== 请求合并

}


